﻿using System;
using System.Reflection;

using Gonte.Reflection.Emit;

namespace Gonte.Reflection
{
    /// <summary>
    /// Provides fast access to a property
    /// </summary>
    public class PropertyAccessor
    {
        #region Constructors

        public PropertyAccessor(PropertyInfo propertyInfo)
        {
            _propertyName = propertyInfo.Name;
            _isPrimitive = PrimitiveType.IsPrimitive(propertyInfo.PropertyType);
            _type = propertyInfo.PropertyType;
            CreateGetter(propertyInfo);
            CreateSetter(propertyInfo);
        }

        #endregion

        #region Overridables

        /// <summary>
        /// Gets the value from the object
        /// </summary>
        /// <param name="object">Object to retrieve the value from</param>
        /// <returns>The value</returns>
        public Object GetValue(Object @object)
        {
            return _getter(@object);
        }

        /// <summary>
        /// Sets the value of the object
        /// </summary>
        /// <param name="object">The object to set the value on</param>
        /// <param name="value">The value to set</param>
        public void SetValue(Object @object, Object value)
        {
            _setter(@object, value);
        }

        #endregion

        #region Properties

        public String PropertyName
        {
            get { return _propertyName; }
        }

        internal bool IsPrimitive
        {
            get { return _isPrimitive; }
        }

        public Type Type
        {
            get { return _type; }
        } 

        public bool CanGet
        {
            get { return _canGet; }
        }

        public bool CanSet
        {
            get { return _canSet; }
        }

        #endregion

        #region Helpers

        private void CreateGetter(PropertyInfo propertyInfo)
        {
            MethodInfo getMethod = propertyInfo.GetGetMethod();

            if (getMethod == null)
            {
                _canGet = false;
                return;
            }

            _getter = Emitter.EmitPropertyGetter(propertyInfo);
            _canGet = true;
        }

        private void CreateSetter(PropertyInfo propertyInfo)
        {
            MethodInfo setMethod = propertyInfo.GetSetMethod();

            if (setMethod == null)
            {
                _canSet = false;
                return;
            }

            _setter = Emitter.EmitPropertySetter(propertyInfo);
            _canSet = true;
        }

        #endregion

        #region Fields

        private readonly String _propertyName;
        private readonly bool _isPrimitive;
        private readonly Type _type;
        private bool _canGet;
        private bool _canSet;
        private Emitter.GetValueHandler _getter;
        private Emitter.SetValueHandler _setter;

        #endregion
    }
}
